//============================================================================
// Name        : main.c
// Author      : Yugi
// Version     : 1.0
// Description : 
//============================================================================

#include <stdlib.h>
#include <stdio.h>
#include <dr_api.h>

#include "worklist.h"
#include "parameter.h"
#include "slicer.h"
#include "cfg.h"
#include "cg.h"
#include "udefine.h"

char path_buf[MAXIMUM_PATH];// path buffer
int dir_len;				// directory path length

/* Output files */
file_t logfile;				// log
file_t cg;					// call graph
file_t cfg;					// control flow graph
file_t backward_cfg;		// control flow graph for backword slice
file_t forward_cfg;			// control flow graph for forward slice

app_pc target;				// Target pc
PWORKLIST bWorklist;		// Backward slicing worklist
PWORKLIST fWorklist;		// Forward slicing worklist

PCFG pcfg;					// Control flow graph
PCG pcg;					// Call graph
instrlist_t *iList;			// Instruction list
	
module_data_t *module;		// Module data


/* Forward declarations */
void init(client_id_t id);
dr_emit_flags_t event_bb_handler(void *, void *, instrlist_t *, bool, bool);
void event_exit_handler(void);

/* Init & register events */
DR_EXPORT void dr_init(client_id_t id)
{
	/* Initializing */
	init(id);

	/* Register events. */
    dr_register_bb_event(event_bb_handler);
    dr_register_exit_event(event_exit_handler);
}

/* Basic block event handler */
dr_emit_flags_t event_bb_handler(void *drcontext, void *tag, 
										instrlist_t *bb, bool for_trace, bool translating){
	static bool fUninit = true;	// Flag for initializing

	if (fUninit){
		/* Initial local fields */
		pcfg = cfg_new();
		pcg = cg_new();
		iList = instrlist_create(drcontext);
		instrlist_init(iList);
		fUninit = false;
	}

	if (init_slice(drcontext, bb, &target, pcfg, iList)){
		/* Print initial slice */
		dr_fprintf(logfile, "Step1:\tinitial slice\n");
		print_initial_slice(drcontext, logfile, iList);

		/* Dynamic slice */
		dr_fprintf(logfile, "\n\nStep2:\tdynamic backward slice\n");
		print_dynamic_slice(drcontext, logfile, iList, dynamic_backward_slice(iList, wl_clone(bWorklist)));
		dr_fprintf(logfile, "\n\tdynamic forward slice\n");
		print_dynamic_slice(drcontext, logfile, iList, dynamic_forward_slice(iList, wl_clone(fWorklist)));

		/* Static slice */
		cfg_static_complete(drcontext, pcfg);
		cfg_print(drcontext, cfg, pcfg, false);
		static_backward_slice(drcontext, pcfg, bWorklist);
		cfg_print(drcontext, backward_cfg, pcfg, true);
		static_forward_slice(drcontext, pcfg, fWorklist);
		cfg_print(drcontext, forward_cfg, pcfg, true);

		cg_print(drcontext, cg, pcg, path_buf, dir_len);
	}

	cg_parse(drcontext, bb, module, pcg);
	return DR_EMIT_DEFAULT;
}

/* Exit event handler */
void event_exit_handler(void){
	/* Flush & close the output files. */
	dr_flush_file(log);
	dr_close_file(log);

	dr_flush_file(cfg);
	dr_close_file(cfg);

	dr_flush_file(backward_cfg);
	dr_close_file(backward_cfg);

	dr_flush_file(forward_cfg);
	dr_close_file(forward_cfg);

	dr_flush_file(cg);
	dr_close_file(cg);
}

/*
* Get parameters from input file. Initializing.
*
* REQUIRE:	Input file input.txt in the directory of client
*
*			Input File Format:
*			backward = %esp,0x0c(%esp)
*			forward = %esp,0x0c(%esp)
*			EOF
*
*			input.txt contains at least three properties:
*			TARGET, FORWORD, BACKWORD (macros)
* MODIFY:	log, cf, cfg
*			target
*			bWorklist
*			fWorklist
*/
void init(client_id_t id){
	file_t infile;				// input file
	int ret;					// just for return value

	/* Get client path */
	memset(path_buf, 0, MAXIMUM_PATH);

	ret = dr_snprintf(path_buf, MAXIMUM_PATH, "%s", dr_get_client_path(id));

	if (ret <= 0){
		dr_messagebox("Client path too long, move it to ROOT");
		exit(PATH_TOO_LONG);
	}

	/* Get directory path */
	dir_len = ret;

	while (*(path_buf+dir_len) != DIRSEP){
		if (!dir_len){
			dr_messagebox("Cannot analysis this path");
			exit(PATH_UNRECOGNIZED);
		}

		dir_len--;
	}

	dir_len++;

	/* Open input file */
	ret = dr_snprintf(path_buf+dir_len, MAXIMUM_PATH-dir_len, INPUT);

	if (ret <= 0){
		dr_messagebox("Client path too long, move it to ROOT");
		exit(PATH_TOO_LONG);
	}
	
	infile = dr_open_file(path_buf, DR_FILE_READ);

	if (infile == INVALID_FILE){
		dr_messagebox("Cannot open input file");
		exit(NO_SUCH_FILE);
	}

	/* Parse input file */
	pa_file(infile);
	dr_close_file(infile);

	/* Get target */
	if (sscanf(pa_property(TARGET), "%x", &target) != 1){
		dr_messagebox("Undefine target address");
		exit(PROPERTY_MISSING);
	}

	/* Get backward worklist */
	bWorklist = pa_operands(pa_property(BACKWORD));

	if (!bWorklist){
		dr_messagebox("Undefine backword input list");
		exit(PROPERTY_MISSING);
	}

	/* Get forward worklist */
	fWorklist = pa_operands(pa_property(FORWORD));

	if (!fWorklist){
		dr_messagebox("Undefine forword input list");
		exit(PROPERTY_MISSING);
	}

	/* Open log file */
	ret = dr_snprintf(path_buf+dir_len, MAXIMUM_PATH-dir_len, LOG);

	if (ret <= 0){
		dr_messagebox("Client path too long, move it to ROOT");
		exit(PATH_TOO_LONG);
	}

	logfile = dr_open_file(path_buf, DR_FILE_WRITE_OVERWRITE);

	/* Open call graph */
	ret = dr_snprintf(path_buf+dir_len, MAXIMUM_PATH-dir_len, CALLGRAPH);

	if (ret <= 0){
		dr_messagebox("Client path too long, move it to ROOT");
		exit(PATH_TOO_LONG);
	}

	cg = dr_open_file(path_buf, DR_FILE_WRITE_OVERWRITE);

	/* Open control flow graph */
	ret = dr_snprintf(path_buf+dir_len, MAXIMUM_PATH-dir_len, CFG);

	if (ret <= 0){
		dr_messagebox("Client path too long, move it to ROOT");
		exit(PATH_TOO_LONG);
	}

	cfg = dr_open_file(path_buf, DR_FILE_WRITE_OVERWRITE);

	/* Open control flow graph for backward slice */
	ret = dr_snprintf(path_buf+dir_len, MAXIMUM_PATH-dir_len, BCFG);

	if (ret <= 0){
		dr_messagebox("Client path too long, move it to ROOT");
		exit(PATH_TOO_LONG);
	}

	backward_cfg = dr_open_file(path_buf, DR_FILE_WRITE_OVERWRITE);

	/* Open control flow graph for forward slice */
	ret = dr_snprintf(path_buf+dir_len, MAXIMUM_PATH-dir_len, FCFG);

	if (ret <= 0){
		dr_messagebox("Client path too long, move it to ROOT");
		exit(PATH_TOO_LONG);
	}

	forward_cfg = dr_open_file(path_buf, DR_FILE_WRITE_OVERWRITE);

	module = dr_lookup_module_by_name(dr_get_application_name());
}